import fs from 'fs';
import { createRequire } from 'module';
import path from 'path';

import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
import * as t from '@babel/types';

import { apiPath, loginApi, noAuthApi, rootPath, storePath } from './config';

const require = createRequire(import.meta.url);

const ignoreApi = noAuthApi.concat(loginApi);
// 处理@/开头的路径
export const resolveAliasPath = (p: string) => {
  if (p.startsWith('@/')) {
    return path.resolve(rootPath, p.slice(2));
  }

  return p;
};

// 处理路径
export const resolvePath = (p: string, from?: string) => {
  if (from && p.startsWith('.')) {
    p = path.resolve(from, p);
  }

  const aliasPath = resolveAliasPath(p);

  try {
    return require.resolve(aliasPath);
  } catch (e) {
    return null;
  }
};

type Deps = {
  path: string;
  specifiers: string[];
  apis: Api[];
  deps: Deps[];
};

export const findDeps = (filePath: string, visited = new Set<string>()): Deps[] => {
  // 防止重复访问同一个文件，避免循环依赖
  if (visited.has(filePath)) {
    return [];
  }
  visited.add(filePath);

  const deps: Deps[] = [];
  const code = fs.readFileSync(filePath, 'utf8');
  const ast = parse(code, {
    sourceType: 'module',
    plugins: ['jsx', 'typescript']
  });

  if (filePath.startsWith(storePath)) {
    return deps;
  }

  (traverse as unknown as { default: typeof traverse }).default(ast, {
    // 导入的依赖
    ImportDeclaration(visitNode) {
      const currentPath = path.dirname(filePath);
      const importPath = resolvePath(visitNode.node.source.value, currentPath);

      if (importPath && importPath.startsWith(rootPath)) {
        const suffix = importPath.split('.').pop();
        const specifiers: string[] = [];

        if (suffix === 'ts' || suffix === 'tsx') {
          // 导入的方法
          visitNode.node.specifiers.forEach((specifier) => {
            if (specifier.type === 'ImportDefaultSpecifier') {
              // specifiers.push('default');
              // return 'default';
            } else if (specifier.type === 'ImportNamespaceSpecifier') {
              // console.log('specifier', specifier)
              // return '*';
            } else if (specifier.type === 'ImportSpecifier') {
              specifiers.push((specifier.imported as { name: string }).name);
            }
          });

          let apis: Api[] = [];

          if (importPath.startsWith(apiPath)) {
            apis = findApi(importPath, specifiers);
          }

          deps.push({
            path: importPath,
            apis,
            specifiers,
            deps: findDeps(importPath, visited) // 递归调用传递 visited 集合
          });
        }
      }
    }
  });

  return deps;
};

type Api = {
  url: string;
  method: string;
  functionName: string;
};

// 找到接口文件下面的请求方法和路径
const findApi = (filePath: string, specifiers: string[]): Api[] => {
  const res: Api[] = [];
  const code = fs.readFileSync(filePath, 'utf8');
  const ast = parse(code, {
    sourceType: 'module',
    plugins: ['jsx', 'typescript']
  });

  (traverse as unknown as { default: typeof traverse }).default(ast, {
    ExportNamedDeclaration(path) {
      const declaration = path.node.declaration;
      if (t.isFunctionDeclaration(declaration) && declaration.async) {
        const functionName = declaration.id?.name;

        if (!functionName) {
          return;
        }

        if (!specifiers.includes(functionName)) {
          return;
        }

        // 查找函数体内的 api 调用
        path.traverse({
          CallExpression(innerPath) {
            const callee = innerPath.node.callee;
            if (t.isIdentifier(callee, { name: 'api' })) {
              const args = innerPath.node.arguments;

              if (t.isTemplateLiteral(args[0])) {
                const url = (args[0] as t.TemplateLiteral).quasis[0].value.cooked;
                const options = args[1] as t.ObjectExpression;

                let method = ''; // 默认值
                options.properties.forEach((prop) => {
                  if (
                    t.isObjectProperty(prop) &&
                    t.isIdentifier(prop.key, { name: 'method' }) &&
                    t.isStringLiteral(prop.value)
                  ) {
                    method = prop.value.value.toLowerCase();
                  }
                });

                if (!url || !method) {
                  return;
                }

                if (res.some((item) => item.url === url && item.method === method)) {
                  return;
                }

                const module = url.split('/')[2];

                if (
                  ignoreApi.some((api) => {
                    const [type, content] = api.split(':');

                    if (type === 'module') {
                      return module === content;
                    }

                    return type == method && url === content;
                  })
                ) {
                  return;
                }

                res.push({
                  functionName,
                  url,
                  method
                });
              }
            }
          }
        });
      }
    }
  });

  return res;
};

export const getDeptsApi = (deps: Deps[]): Api[] => {
  const result: Api[] = [];

  deps.forEach((dep) => {
    dep.apis.forEach((api) => {
      if (result.some((item) => item.url === api.url && item.method === api.method)) {
        return;
      }

      result.push(api);
    });

    getDeptsApi(dep.deps).forEach((item) => {
      if (result.some((item2) => item2.url === item.url && item2.method === item.method)) {
        return;
      }

      result.push(item);
    });
  });

  return result;
};
